package com.ly.main;

import com.ly.rule.LyServlet;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 扫描所有的servlet，放进map
 *
 * @author liuyang
 */
public class LyCatServer {

    /**
     * 放入已经实现了LyServlet的实例
     */
    private final Map<String, LyServlet> servletMap = new ConcurrentHashMap<>();
    /**
     * 放名称
     */
    private final Map<String, String> nameMap = new HashMap<>();

    /**
     * 要扫描项目的位置
     */
    private final String location;

    /**
     * 端口
     */
    private final int port;

    public LyCatServer(String location, int port) {
        this.location = location;
        this.port = port;
    }

    public void start() throws Exception {
        // 加载指定包中的所有Servlet的类名
        this.pushServlet(location);
        // 启动server服务
        this.startServer();
    }

    private void pushServlet(String location) {
        // 通过类加载器拿到加载资源的对象
        URL resource = this.getClass().getClassLoader().getResource(location.replaceAll("\\.", "/"));
        System.out.println(resource);
        if (resource == null) {
            return;
        }
        // 将URL变成File资源
        File file = new File(resource.getFile());
        // 拿到这个文件下所有的文件之后，查找所有的class文件
        for (File f : file.listFiles()) {
            // 如果是目录则递归调用
            if (f.isDirectory()) {
                this.pushServlet(location + "." + f.getName());
            } else if (f.getName().endsWith(".class")) {
                String simpleName = f.getName().replace(".class", "").trim();
                nameMap.put(simpleName.toLowerCase(), location + "." + simpleName);
            }
        }
    }

    private void startServer() throws InterruptedException {

        // 通过netty的nio实现
        NioEventLoopGroup parent = new NioEventLoopGroup();
        NioEventLoopGroup child = new NioEventLoopGroup();

        try {
            ServerBootstrap server = new ServerBootstrap();

            server.group(parent, child)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    // 指定是否启用心跳机制来检测长连接的存活性，即客户端的存活性
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {
                            ChannelPipeline pipeline = sc.pipeline();
                            pipeline.addLast(new HttpServerCodec());
                            // handler执行的时候，自己写的doGet()就执行了
                            pipeline.addLast(new LyCatHandler(nameMap, servletMap));
                        }
                    });
            ChannelFuture future = server.bind(port).sync();
            System.out.println("lycat start success, port:" + port);
            future.channel().closeFuture().sync();

        } finally {
            parent.shutdownGracefully();
            child.shutdownGracefully();
        }
    }
}
